import java.util.LinkedList;
import java.util.Scanner;

public class Aging{
	
	LinkedList<String> list;
	AgingNode[] pageTable;
	String RBits=null;
	int clockPeriod=0,clockPeriodCounter=0,pageFaultCounter=0;
	Scanner scanner;
	int inRam=0,numberOfFrames=0;
		public Aging(LinkedList<String> list,int clockPeriod,int numberOfFrames){
			pageTable= new AgingNode[numberOfFrames];
			this.numberOfFrames=numberOfFrames;
			this.list=list;
			this.clockPeriod=clockPeriod;
			System.out.println("Applying Aging for page replacement");
			System.out.println("RAM="+numberOfFrames+" page frames");
			System.out.println("Clock Period= "+clockPeriod+" page references");
			for(int i=0; i<pageTable.length;i++){
				pageTable[i]=new AgingNode();
			}
			//set the length of Rbits to the number of frames
			setRBitLength();//this can also be use to clear the Rbits
			for(int i=1;i<list.size(); i++){
				String command = list.get(i);
				scanner=new Scanner(command);
				String letter=scanner.next();
				int pageNumber=scanner.nextInt();
				
				inRam=isPageInRam(pageNumber);
				if(inRam!=-1){ //the page in memory lets set the Rbit of that page to 1
				 setRBit(inRam);
				 System.out.println(letter+" "+pageNumber+ ": in RAM");
				}
				else{//the page is not in ram find open slot
					int openSlot=findOpenSlot();
					if(openSlot!=-1){//there is a slot in page table lets use it
						pageTable[openSlot].pageNumber=pageNumber;
						setRBit(openSlot);
						System.out.println(letter+" "+pageNumber+ ": page fault");
						pageFaultCounter++;//count the number of page faults
					}
					else{//the memory is filled lets evict a page;
						int pageToEvict=pageToEvict();
						int temp=pageTable[pageToEvict].pageNumber;
						pageTable[pageToEvict].pageNumber=pageNumber;
						pageTable[pageToEvict].counter="00000";
						setRBit(pageToEvict);
						System.out.println(letter+" "+pageNumber+ ": page fault");
						pageFaultCounter++;//count the number of page faults
						System.out.println("  page "+temp +" evicted!");
						
					}
					
				}
				clockPeriodCounter++;
				if(clockPeriodCounter==clockPeriod){
					System.out.println("<<<Time to clear R bits>>>");
					clockPeriodCounter=0;
					shiftCounters();
					addRBits();
					setRBitLength();
				}
				
			}
			System.out.println("Total number of page faults: "+pageFaultCounter);
		}
		
		private void addRBits() {
			for(int i =0;i<RBits.length();i++){
				String bit=RBits.substring(i, i+1);
				String temp=pageTable[i].counter.substring(1, pageTable[i].counter.length());
				pageTable[i].counter=bit+temp;
			}
			
		}

		private void shiftCounters() {
			for(int i=0;i<pageTable.length;i++){
				String temp1=pageTable[i].counter;
				String temp2=temp1.substring(0, temp1.length()-1);
				String temp3=temp1.substring(temp1.length()-1, temp1.length());
				pageTable[i].counter=temp3+temp2;
			}
			
		}

		private int pageToEvict() {
			int min=31;
			int index=0;
			int temp=0;
			for(int i=0;i<pageTable.length;i++){
				temp=Integer.parseInt(pageTable[i].counter, 2);
				if(temp<min){
					min=temp;
					index=i;
				}
			}
			return index;
		}

		private int findOpenSlot() {
			
			for (int i=0;i<pageTable.length;i++){
				if(pageTable[i].pageNumber==-1)return i;
			}
			return -1;
		}

		private void setRBitLength() {
			RBits=null;
			for(int i=0;i<numberOfFrames;i++){
				if(i==0)RBits="0";
				else RBits=RBits+"0";
			}
			
		}

		private void setRBit(int inRam) {//use to set a specified RBit to 1
			
			if(inRam==0&&RBits.length()==1)RBits="1";
			
			else{
				String temp1=RBits.substring(0,inRam);
				String temp2=RBits.substring(inRam, inRam+1);
				String temp3=RBits.substring(inRam+1, RBits.length());
				temp2="1";
				RBits=temp1+temp2+temp3;
			}
		}

		private int isPageInRam(int pageNumber){
			for(int i=0;i<pageTable.length;i++){
				if(pageTable[i].pageNumber==pageNumber)return i;
			}
			return -1;
		}
		
}